﻿using System;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;

namespace Bookvar.UI
{
    /// <summary>
    /// Interaction logic for VideoPlayer.xaml
    /// </summary>
    public partial class VideoPlayer
    {
        private readonly MediaTimeline videoTimeline;
        private readonly Storyboard videoStoryboard;
        private Window fullScreenWindow;
        private bool shouldUpdateTimeline = true;
        private bool shouldBegin = true;

        #region Source

        public Uri Source
        {
            set { SetValue( SourceProperty, value ); }
            get { return (Uri) GetValue( SourceProperty ); }
        }

        public static readonly DependencyProperty SourceProperty =
            DependencyProperty.Register( "Source", typeof ( Uri ), typeof ( VideoPlayer ),
                                         new UIPropertyMetadata( OnSourceChanged ) );

        public static void OnSourceChanged( DependencyObject sender, DependencyPropertyChangedEventArgs args )
        {
            VideoPlayer player = (VideoPlayer) sender;
            player.VideoWindow.Source = (Uri) args.NewValue;
            player.videoTimeline.Source = (Uri) args.NewValue;
        }

        #endregion

        #region IsFullScreen

        public static readonly DependencyProperty IsFullScreenProperty =
            DependencyProperty.Register( "IsFullScreen", typeof ( bool ), typeof ( VideoPlayer ),
                                         new FrameworkPropertyMetadata( BooleanBoxes.FalseBox, IsFullScreenChanged ) );

        public bool IsFullScreen
        {
            get { return (bool) GetValue( IsFullScreenProperty ); }
            set { SetValue( IsFullScreenProperty, value ); }
        }

        private static void IsFullScreenChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
        {
            VideoPlayer player = (VideoPlayer) sender;
            player.OnIsFullScreenChanged();
        }

        #endregion

        #region IsPlaying

        public static readonly DependencyProperty IsPlayingProperty =
            DependencyProperty.Register( "IsPlaying", typeof ( bool ), typeof ( VideoPlayer ),
                                         new FrameworkPropertyMetadata( BooleanBoxes.FalseBox ) );

        public bool IsPlaying
        {
            get { return (bool) GetValue( IsPlayingProperty ); }
            set { SetValue( IsPlayingProperty, value ); }
        }

        #endregion

        #region SeekOffset

        public static readonly DependencyProperty SeekOffsetProperty =
            DependencyProperty.Register( "SeekOffset", typeof ( double ), typeof ( VideoPlayer ),
                                         new FrameworkPropertyMetadata( 5d ) );

        public double SeekOffset
        {
            get { return (double) GetValue( SeekOffsetProperty ); }
            set { SetValue( SeekOffsetProperty, value ); }
        }

        #endregion

        private static void OnHeightChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
        {
            double height = (double) e.NewValue;
            VideoPlayer player = (VideoPlayer) d;
            ScaleTransform scale = (ScaleTransform) player.Main.LayoutTransform;

            if ( height > 0 )
            {
                scale.ScaleY = height / (double) HeightProperty.GetMetadata( player ).DefaultValue;
            }
        }

        private static void OnWidthChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
        {
            double width = (double) e.NewValue;
            VideoPlayer player = (VideoPlayer) d;
            ScaleTransform scale = (ScaleTransform) player.Main.LayoutTransform;

            if ( width > 0 )
            {
                scale.ScaleX = width / (double) WidthProperty.GetMetadata( player ).DefaultValue;
            }
        }

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")]
		static VideoPlayer()
        {
			WidthProperty.OverrideMetadata(typeof(VideoPlayer),
											new FrameworkPropertyMetadata(640d, OnWidthChanged));
			HeightProperty.OverrideMetadata(typeof(VideoPlayer),
											 new FrameworkPropertyMetadata(480d, OnHeightChanged));
        }
        public VideoPlayer()
        {

            InitializeComponent();

            Main.CommandBindings.Add( new CommandBinding( MediaCommands.Play, ExecutePlayCommand, CanExecuteAlways ) );
            Main.CommandBindings.Add( new CommandBinding( MediaCommands.Pause, ExecutePauseCommand, CanExecuteAlways ) );
            Main.CommandBindings.Add( new CommandBinding( MediaCommands.Stop, ExecuteStopCommand, CanExecuteAlways ) );
            Main.CommandBindings.Add(
                new CommandBinding( MediaCommands.NextTrack, ExecuteNextCommand, CanExecuteAlways ) );
            Main.CommandBindings.Add(
                new CommandBinding( MediaCommands.PreviousTrack, ExecutePreviousCommand, CanExecuteAlways ) );
            Main.CommandBindings.Add(
                new CommandBinding( MediaCommands.MuteVolume, ExecuteMuteCommand, CanExecuteAlways ) );

            VideoWindow.MediaOpened += OnVideoOpened;
            VideoWindow.MediaEnded += OnVideoEnded;
            Main.MouseLeftButtonDown += OnMainMouseLeftButtonDown;

            videoStoryboard = (Storyboard) Main.FindResource( "VideoStoryboard" );
            videoTimeline = (MediaTimeline) videoStoryboard.Children[ 0 ];
            videoTimeline.CurrentTimeInvalidated += OnVideoTimeInvalidated;

            Loaded += OnLoaded;
        }

        private void OnMainMouseLeftButtonDown( object sender, MouseButtonEventArgs e )
        {
            if ( e.ClickCount == 2 )
            {
                IsFullScreen = !IsFullScreen;
            }
        }

        private void OnLoaded( object sender, RoutedEventArgs e )
        {
            Track track = (Track) TimelineSlider.Template.FindName( "PART_Track", TimelineSlider );
            track.Thumb.DragStarted += OnTimelineDragStarted;
            track.Thumb.DragCompleted += OnTimelineDragCompleted;
        }

        private void OnTimelineDragCompleted( object sender, DragCompletedEventArgs e )
        {
            shouldUpdateTimeline = true;
            videoStoryboard.SeekAlignedToLastTick( VideoWindow, TimeSpan.FromMilliseconds( TimelineSlider.Value ),
                                                   TimeSeekOrigin.BeginTime );
            if ( IsPlaying )
            {
                videoStoryboard.Resume( VideoWindow );
            }
            else
            {
                videoStoryboard.Pause( VideoWindow );
            }
        }

        private void OnTimelineDragStarted( object sender, DragStartedEventArgs e )
        {
            shouldUpdateTimeline = false;
            videoStoryboard.Pause( VideoWindow );
        }

        private void OnVideoTimeInvalidated( object sender, EventArgs e )
        {
            if ( VideoWindow.Clock.CurrentTime.HasValue && shouldUpdateTimeline )
            {
                TimelineSlider.Value = VideoWindow.Clock.CurrentTime.Value.TotalMilliseconds;
            }
        }

        private void OnVideoEnded( object sender, RoutedEventArgs e )
        {
            SetStopState();
        }

        private void SetStopState()
        {
            IsPlaying = false;
            shouldBegin = true;
            TimelineSlider.Value = TimelineSlider.Minimum;
            PlayPauseToggleButton.IsChecked = false;
        }


        private void OnVideoOpened( object sender, RoutedEventArgs e )
        {
            TimelineSlider.Maximum = VideoWindow.Clock.NaturalDuration.TimeSpan.TotalMilliseconds;
        }

        private void PlayPauseButtonClicked( object sender, RoutedEventArgs e )
        {
            if ( PlayPauseToggleButton.IsChecked == true )
            {
                PlayPauseToggleButton.Command = MediaCommands.Play;
            }
            else
            {
                PlayPauseToggleButton.Command = MediaCommands.Pause;
            }
        }

        private void ExecutePauseCommand( object sender, ExecutedRoutedEventArgs e )
        {
            videoStoryboard.Pause( VideoWindow );
            IsPlaying = false;
        }

        private void ExecuteMuteCommand( object sender, ExecutedRoutedEventArgs e )
        {
            VideoWindow.IsMuted = !VideoWindow.IsMuted;
        }

        private void ExecutePlayCommand( object sender, ExecutedRoutedEventArgs e )
        {
            if ( shouldBegin )
            {
                videoStoryboard.Begin( VideoWindow, true );
                shouldBegin = false;
            }
            else
            {
                videoStoryboard.Resume( VideoWindow );
            }

            IsPlaying = true;
        }

        private void ExecuteNextCommand( object sender, ExecutedRoutedEventArgs e )
        {
            TimeSpan position = VideoWindow.Position + TimeSpan.FromSeconds( SeekOffset );
            if ( position <= VideoWindow.Clock.NaturalDuration )
            {
                videoStoryboard.SeekAlignedToLastTick( VideoWindow, position, TimeSeekOrigin.BeginTime );
            }
        }

        private void ExecutePreviousCommand( object sender, ExecutedRoutedEventArgs e )
        {
            TimeSpan position = VideoWindow.Position - TimeSpan.FromSeconds( SeekOffset );
            if ( position < TimeSpan.Zero )
            {
                position = TimeSpan.Zero;
            }

            videoStoryboard.SeekAlignedToLastTick( VideoWindow, position, TimeSeekOrigin.BeginTime );
        }

        private void CanExecuteAlways( object sender, CanExecuteRoutedEventArgs e )
        {
            e.CanExecute = true;
        }

        private void ExecuteStopCommand( object sender, ExecutedRoutedEventArgs e )
        {
            videoStoryboard.Stop( VideoWindow );
            SetStopState();
        }

        protected virtual void OnIsFullScreenChanged()
        {
            if ( IsFullScreen )
            {
                fullScreenWindow = new Window();
                fullScreenWindow.WindowState = WindowState.Maximized;
                fullScreenWindow.WindowStyle = WindowStyle.None;
                fullScreenWindow.Background = new VisualBrush( VideoWindow );
                fullScreenWindow.Owner = Application.Current.MainWindow;

                fullScreenWindow.MouseDoubleClick += OnFullScreenWindowMouseDoubleClick;
                fullScreenWindow.KeyDown += OnFullScreenWindowKeyDown;

                fullScreenWindow.Show();
                
            }
            else
            {
                fullScreenWindow.Close();
                fullScreenWindow = null;
            }
        }

        private void OnFullScreenWindowKeyDown( object sender, KeyEventArgs e )
        {
            if ( !e.Handled )
            {
                if ( e.Key == Key.Escape )
                {
                    if ( IsFullScreen )
                    {
                        IsFullScreen = false;
                        e.Handled = true;
                    }
                }
            }
        }

        private void OnFullScreenWindowMouseDoubleClick( object sender, MouseButtonEventArgs e )
        {
            IsFullScreen = false;
        }
    }
}